package com.hw.saweb.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.basic.SaBasicUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.hw.sacommon.Constant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Arrays;

/**
 * @program: sa-token-start
 * @description:
 * @author: hewei
 * @date: 2023-09-05 14:47
 * 手动权限校验 Controller
 **/

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    HttpSession session;
    @Autowired
    HttpServletRequest request;
    @Autowired
    HttpServletResponse response;

     /**
      * 测试登录，浏览器访问： http://localhost:9000/user/doLogin?username=sa&password=123456
      *
      * `SaLoginModel`为登录参数Model，其有诸多参数决定登录时的各种逻辑，例如：
      * StpUtil.login(10001, new SaLoginModel()
      *             .setDevice("PC")                // 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
      *             .setIsLastingCookie(true)        // 是否为持久Cookie（临时Cookie在浏览器关闭时会自动删除，持久Cookie在重新打开后依然存在）
      *             .setTimeout(60 * 60 * 24 * 7)    // 指定此次登录token的有效期, 单位:秒 （如未指定，自动取全局配置的 timeout 值）
      *             .setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录的生成的Token
      *             .setIsWriteHeader(false)         // 是否在登录后将 Token 写入到响应头
      *             );
      * */
    @RequestMapping("doLogin")
    public SaResult doLogin(String username, String password) {
        // 校验指定账号是否已被封禁，如果被封禁则抛出异常 `DisableServiceException`
        StpUtil.checkDisable(Constant.USER_ID);
        // 解除封禁
//        StpUtil.untieDisable(Constant.USER_ID);
        // 此处仅作模拟示例，真实项目需要从数据库中查询数据进行比对
        if(!StpUtil.isDisable(Constant.USER_ID) && "sa".equals(username) && "123456".equals(password)) {
            // 记住我功能：设置登录账号id为Constant.USER_ID，第二个参数指定是否为[记住我]，默认为true。此值为false后，关闭浏览器后再次打开需要重新登录
//            StpUtil.login(Constant.USER_ID, false);
            StpUtil.login(Constant.USER_ID);
            SaTokenInfo tokenInfo=StpUtil.getTokenInfo();
            String token=StpUtil.getTokenValueByLoginId(Constant.USER_ID);
            log.info("token[{}]: {}" ,token,tokenInfo);
            return SaResult.ok("登录成功");
        }
        return SaResult.error("登录失败");
    }

    // 查询登录状态，浏览器访问： http://localhost:9000/user/isLogin
    @RequestMapping("isLogin")
    public String isLogin() {
        Cookie[] cookies = request.getCookies();
        Arrays.stream(cookies).forEach(cookie -> {System.out.println(cookie.getName() + "=" + cookie.getValue());});
        response.getHeaderNames().forEach(name -> {System.out.println(name);});
        log.info("session:{}",session.getId());
        return "当前会话是否登录：" + StpUtil.isLogin();
    }

    // 查询 Token 信息  ---- http://localhost:9000/user/tokenInfo
    @RequestMapping("tokenInfo")
    public SaResult tokenInfo() {
        log.info(StpUtil.getTokenInfo().toString());
        return SaResult.data(StpUtil.getTokenInfo());
    }

    // 测试注销  ---- http://localhost:9000/user/logout
    @SaCheckLogin
    @RequestMapping("logout")
    public SaResult logout() {
        StpUtil.logout();

//        StpUtil.logout(10001);                    // 强制指定账号注销下线
//        StpUtil.logout(10001, "PC");              // 强制指定账号指定端注销下线
//        StpUtil.logoutByTokenValue("token");      // 强制指定 Token 注销下线

//        StpUtil.kickout(10001);                    // 将指定账号踢下线
//        StpUtil.kickout(10001, "PC");              // 将指定账号指定端踢下线
//        StpUtil.kickoutByTokenValue("token");      // 将指定 Token 踢下线

        return SaResult.ok();
    }

    /**
     * // 获取账号10000是否含有指定角色标识
     * StpUtil.hasRole(10000, "admin");
     * // 获取账号10000是否含有指定权限码
     * StpUtil.hasPermission(10000, "user.add");
     */
    @RequestMapping("/checkPermission/{tag}/{permission}")
    public String checkPermission(@PathVariable("tag") String tag, @PathVariable("permission") String permission) {
        switch(tag) {
            case "or":
                StpUtil.checkPermissionOr(permission.split(","));
                break;
            case "and":
                StpUtil.checkPermissionAnd(permission.split(","));
                break;
            default:
                StpUtil.checkPermission(permission);
        }

        return "已授权的！";
    }

    @RequestMapping("/checkRole/{tag}/{role}")
    public String checkRole(@PathVariable("tag") String tag,@PathVariable("role") String role) {
        switch(tag) {
            case "or":
                StpUtil.checkRoleOr(role.split(","));
                break;
            case "and":
                StpUtil.checkRoleAnd(role.split(","));
                break;
            default:
                StpUtil.checkRole(role);
        }
        return "已授权的！";
    }

     /*
     删除用户,只有标识为 "USER" 的二级认证通过之后才可以执行删除
     可以通过代码 StpUtil.isSafe(Constant.SAFE_TAG) 进行验证，也可以通过 @SaCheckSafe(Constant.SAFE_TAG) 注解验证
     */
//     @SaCheckSafe(Constant.SAFE_TAG)
    @RequestMapping("delete")
    public SaResult delete(String name) {
        // 第1步，先检查当前会话是否已完成二级认证,如果没有完成认证或者认证结果过时就返回异常。
        if(!StpUtil.isSafe(Constant.SAFE_TAG) || StpUtil.getSafeTime(Constant.SAFE_TAG) < 0) {
            return SaResult.error("用户删除失败，请完成二级认证后再次访问接口");
        }

        // 第2步，如果已完成二级认证，则开始执行业务逻辑
        // ...

        // 第3步，业务完成关闭二级认证，返回结果
//        StpUtil.closeSafe(Constant.SAFE_TAG);
        return SaResult.ok("用户删除成功");
    }

    // 提供密码进行二级认证
    @RequestMapping("openSafe")
    public SaResult openSafe(String password) {
        // 比对密码（此处只是举例，真实项目时可拿其它参数进行校验）
        if("123456".equals(password)) {

            // 比对成功，为当前会话打开二级认证，有效期为120秒
            StpUtil.openSafe(Constant.SAFE_TAG,120);
            return SaResult.ok("二级认证成功");
        }
        // 如果密码校验失败，则二级认证也会失败
        return SaResult.error("二级认证失败");
    }

    /**
     * 正在登录的账号，将其封禁并不会使它立即掉线，如果我们需要它即刻下线，可采用先踢再封禁的策略
     * */
    @RequestMapping("/disable")
    public SaResult disable(){

        // 先踢下线
//        StpUtil.kickout(Constant.USER_ID);
        // 封禁指定账号60秒
        StpUtil.disable(Constant.USER_ID, 60);
        log.info("账号封禁：{}",StpUtil.isDisable(Constant.USER_ID));

        // 封禁指定用户 10001 的评论能力，期限为 1天,"comment"是评论功能标识
//        StpUtil.disable(10001, "comment", 86400);
        // 检验评论功能封禁情况，会抛出异常：`DisableServiceException`，使用 e.getService() 可获取业务标识 `comment`
//        StpUtil.checkDisable(10001, "comment");
        // 解封：指定账号的指定服务
//        StpUtil.untieDisable(10001, "<业务标识>");
        // 获取：指定账号的指定服务 剩余封禁时间，单位：秒（-1=永久封禁，-2=未被封禁）
//        StpUtil.getDisableTime(10001, "<业务标识>");
        // 分类阶梯封禁，参数：封禁账号、封禁服务、封禁级别、封禁时间
//        StpUtil.disableLevel(10001, "comment", 3, 10000);

        return SaResult.ok("账号已封禁！");
    }

    // 以注解方式启用 Basic 校验
//    @SaCheckBasic(account = "sa:123456")
    @RequestMapping("/basic")
    public SaResult basic(){
        // 对当前会话进行 Basic 校验，账号密码为 yml 配置的值（例如：sa-token.basic=sa:123456）,也可以通过注解认证
        SaBasicUtil.check();

        // 对当前会话进行 Basic 校验，账号密码为：`sa / 123456`
//        SaBasicUtil.check("sa:123456");
        return SaResult.ok("basic 认证成功！");
    }


}

